package com.ssm.subject.domain.redis;

import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.util.Optionals;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Redis工具类
 */
@Component
public class RedisUtil {
    @Resource
    private RedisTemplate redisTemplate;

    private static final String CACHE_KEY_SEPARATOR = ".";

    /**
     * 构建缓存key
     * String... ： 可变参数，参数个数为0个或多个
     * Stream.of 字符数组转化为stream流
     * collect：收集器，终止操作；通过Collectors类把字符数组内的元素以.进行拼接
     */
    public String buildKey(String... strObjs) {
        return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR));
    }

    /**
     * 是否存在key
     * @param key
     * @return
     */
    public boolean exist(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除key
     * @param key
     * @return
     */
    public boolean del(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * set(无过期时间)
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * setNx
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     * @return
     */
    public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);
    }

    /**
     * 获取String类型缓存
     * @param key
     * @return
     */
    public String get(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    /**
     * 有序集合 Zadd key score1 member1 [score2 member2] 向有序集合添加一个 / 多个 成员
     */
    public Boolean zAdd(String key, String value, Long score) {
        return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));
    }

    /**
     * 获取有序集合对象数量
     */
    public Long countZset(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * Zrange key start stop 通过索引区间返回指定区间内的成员
     */
    public Set<String> rangeZset(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * Zrem key member1移除一个成员
     */
    public Long removeZset(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * Zrem key member1 [member2] ... 移除多个成员
     */
    public void removeZsetList(String key, Set<String> value) {
        value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));
    }

    /**
     * 获取有序列表 某个元素的分数
     */
    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 获取有序集合中指定分数范围内的元素
     */
    public Set<String> rangeByScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));
    }

    /**
     * Zincrby key increment member 给有序列表中的 某个元素添加分数
     * 当key不存在，会创建新的zset
     */
    public Object addScore(String key, Object obj, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, obj, score);
    }

    /**
     * 获取有序列表 某个元素的分数排名
     */
    public Object rank(String key, Object obj) {
        return redisTemplate.opsForZSet().rank(key, obj);
    }

    /**
     * 获取有序集合中的元素及其分数
     * TypedTuple 接口包含了两个重要的方法：
     * getValue()：用于获取元素的值，在这种情况下是 String 类型。
     * getScore()：用于获取元素在有序集合中的分数，通常是 Double 类型。
     */
    public Set<ZSetOperations.TypedTuple<String>> rankWithScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
    }

    /**
     * Hset key hashKey hashValue (hash类型存数据)
     * @param key
     * @param hashKey
     * @param hashValue
     */
    public void putHash(String key, String hashKey, Object hashValue) {
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
    }

    /**
     * 获取int类型缓存
     * @param key
     * @return
     */
    public Integer getInt(String key) {
        return (Integer) redisTemplate.opsForValue().get(key);
    }

    /**
     * 对指定key对应的value值加count（key不存在时会创建key，count为初始值）
     * @param key
     */
    public void increment(String key, Integer count) {
        redisTemplate.opsForValue().increment(key, count);
    }

    /**
     * 获取并删除hash类型缓存并组装为Map
     * scan(key, ScanOptions.NONE)：扫描指定key的hash表；扫描选项,NONE 表示使用默认的扫描行为,即不限制扫描的字段数量，也不使用正则表达式匹配字段。。
     */
    public Map<Object, Object> getHashAndDelete(String key) {
        Map<Object, Object> map = new HashMap<>();
        Cursor<Map.Entry<Object, Object>> scan = redisTemplate.opsForHash().scan(key, ScanOptions.NONE);
        while (scan.hasNext()) { //检查游标（scan）是否还有下一个元素。
            Map.Entry<Object, Object> entry = scan.next(); //获取游标中的下一个元素
            map.put(entry.getKey(), entry.getValue());
            redisTemplate.opsForHash().delete(key, entry.getKey());
        }
        return map;
    }
}
